local meta = {
    __newindex = function(self, key, val)
        local set = rawget(self, "__sets")[key]
        if set then
            set(self, key, val)
            return true
        end
        rawset(self, key, val)
    end,
    __index = function(self, key)
        local get = rawget(self, "__gets")[key]
        return get and get(self, key) or rawget(self, key)
    end
}
local addComponent = function(self, cmp, ...)
    if not cmp then
        Console.error("addComponent: invalid component")
        return self
    end
    if self._components[cmp] then
        return self
    end
    self._components[cmp] = true
    if cmp.dependencies then
        for i, d in ipairs(cmp.dependencies) do
            if not self._components[d] then
                self:addComponent(d)
            end
        end
    end

    if cmp.props then
        for k, v in pairs(cmp.props) do
            if self[k] ~= nil and type(v) ~= "function" then
                Console.error("Entity:addComponent", "property", k, "is already defined in", self.name)
            end
            self[k] = v
        end
    end

    for k, v in pairs(cmp) do
        if type(v) ~= "table" and k ~= "ctor" and k ~= "name" then
            if self[k] ~= nil and type(v) ~= "function" then
                Console.error("Entity:addComponent", "function", k, "is already defined in", self.name, "by", v)
            end
            self[k] = v
        end
    end

    if cmp.gets then
        for k, v in pairs(cmp.gets) do
            rawget(self, "__gets")[k] = v
        end
    end

    if cmp.sets then
        for k, v in pairs(cmp.sets) do
            if rawget(self, k) then
                Console.warn("Entity:addComponent", "property", k, "is already defined in", self.name)
            end
            rawget(self, "__sets")[k] = v
        end
    end

    if cmp.ctor then
        cmp.ctor(self, ...)
    end
    return self
end

local function hasCompoent(self, cmp)
    return self._components[cmp]
end
local function removeComponent(self, cmp)
    if not cmp then
        Console.error("addComponent: invalid component")
        return self
    end
    if not self._components[cmp] then
        Console.warn("Entity:removeComponent", "component", cmp.name, "is not added to", self.name)
        return self
    end

    -- TODO
end

local function addTag(self, tag)
    table.insert(self._tags, tag)
end

local function addTags(self, ...)
    for _, t in ipairs({...}) do
        table.insert(self._tags, t)
    end
end
local function hasTag(self, tag)
    return table.hasElement(self._tags, tag)
end

local function removeTag(self, tag)
    for i = #self._tags, 1, -1 do
        if self._tags[i] == tag then
            table.remove(self._tags, i)
            break
        end
    end
end
local function removeTags(self, ...)
    for _, t in ipairs({...}) do
        removeTag(self, t)
    end
end
Entity = {}
function Entity.new()
    return setmetatable({
        __gets = {}, __sets = {}, _components = {}, _componentList = {}, _tags = {},
        addComponent = addComponent, hasCompoent = hasCompoent, removeComponent = removeComponent,
        addTag = addTag, hasTag = hasTag, removeTag = removeTag, removeTags = removeTags, addTags = addTags
    }, meta)
end
